package com.neusoft.tmall.config;

import com.neusoft.tmall.security.DynamicAccessDecisionVoter;
import com.neusoft.tmall.security.DynamicSecurityMetadataSource;
import com.neusoft.tmall.security.JwtAccessDeniedHandler;
import com.neusoft.tmall.security.JwtAuthenticationEntryPoint;
import com.neusoft.tmall.security.JwtAuthenticationTokenFilter;
import com.neusoft.tmall.ums.service.UmsAdminService;
import com.neusoft.tmall.ums.service.UmsResourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import java.util.Arrays;
import java.util.List;

@Configuration
@EnableWebSecurity
public class TmallSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    @Autowired
    private JwtAccessDeniedHandler jwtAccessDeniedHandler;
    @Autowired
    private UmsResourceService umsResoourceService;
    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry reg = http.authorizeRequests();
        for (String item: ignoreUrlsConfig.getUrls()){
//            System.out.println("url为："+item);
            reg.antMatchers(item);
        }
        //授权定制规则，你能干什么
        //设置白名单，即不需要Security做处理的
        //需要做处理的，针对的都是请求
//        super.configure(http);//默认处理，效果同下方源码
        System.out.println("授权规则");
        http.authorizeRequests()
//                .antMatchers("/admin/hello").hasRole("hello")
//                .antMatchers("/admin/welcome").hasRole("admin")
//                .antMatchers("/admin/login").permitAll()
                //任何请求都需要处理
                .anyRequest().authenticated()
                //动态鉴权
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                            @Override
                            public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                                object.setSecurityMetadataSource(new DynamicSecurityMetadataSource(umsResoourceService));
                                object.setAccessDecisionManager(new AffirmativeBased(getDecisionVoters()));
                                return object;
                            }
                        })
                .and()
                .csrf().disable()
                //关闭session管理
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling()
                .accessDeniedHandler(jwtAccessDeniedHandler)
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                ;
//
//                .and()
//                .formLogin().and()
//                .httpBasic();
        //定制请求规则，即授权
//        http.authorizeRequests()
//                .antMatchers("/admin/hello").permitAll()
//                .antMatchers("/admin/login").permitAll()//如果是请求/admin/hello就直接放行
//                .and()
//               .csrf().disable(); // 允许post请求
    }

    /*
    不生成默认密码的情况
        1. 在Security配置类中是否重写了configure(AuthenticationManagerBuilder auth)
        2. 是否有类继承了UserDetailsService
     */
    @Autowired
    UmsAdminService umsAdminService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        super.configure(auth);
        //定义认证规则，用户登录，证明你是谁
        //实名认证
        /*
        在security中，UserDetails用户属性要求三个属性
        用户名username
        密码password，默认是要求加密的，如果不想加密，在内容加上{noop}
        角色role
         */
//        try{
////            auth.inMemoryAuthentication().withUser("admin").password("{noop}admin").roles("admin");
//            auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("admin")).roles("admin");
//        }catch (Exception e){
//            e.printStackTrace();
//        }
//        auth.userDetailsService(umsAdminService).passwordEncoder(passwordEncoder());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
        //平时用的不多
        //设置白名单，即不需要Security做处理的，主要是针对静态资源
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        //BCryptPasswordEncoder: hash加密。对相同的字符串加密后的内容每次都不一样
        return new BCryptPasswordEncoder();
    }

    private List<AccessDecisionVoter<? extends Object>> getDecisionVoters(){
        return Arrays.asList(
                new WebExpressionVoter(),
                new DynamicAccessDecisionVoter(ignoreUrlsConfig)
        );
    }
}
